package com.gzz.orchid.common.webmvc.configuration;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.gzz.orchid.common.core.support.IBaseEnum;
import com.gzz.orchid.common.core.tuple.DateRange;
import com.gzz.orchid.common.core.tuple.DateTimeRange;
import com.gzz.orchid.common.core.util.I18nUtil;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;
import java.util.TimeZone;

/**
 * @author higmd
 */
@Configuration
public class JacksonConfig {
    private final static DateTimeFormatter DE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private final static DateTimeFormatter DT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 方法一
     * Jackson全局转化long类型为String，解决jackson序列化时long类型缺失精度问题
     *
     * @return Jackson2ObjectMapperBuilderCustomizer 注入的对象
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> builder
                .serializerByType(Long.class, ToStringSerializer.instance)
                .serializerByType(Long.TYPE, ToStringSerializer.instance)
                .serializerByType(BigInteger.class, ToStringSerializer.instance)
                .serializerByType(BigDecimal.class, ToStringSerializer.instance)
                .serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DT_FORMATTER))
                .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DT_FORMATTER))
                .serializerByType(LocalDate.class, new LocalDateSerializer(DE_FORMATTER))
                .deserializerByType(LocalDate.class, new LocalDateDeserializer(DE_FORMATTER))
                //枚举类型
                .serializerByType(IBaseEnum.class, new JsonSerializer<IBaseEnum>() {
                    @Override
                    public void serialize(IBaseEnum e, JsonGenerator gen, SerializerProvider provider) throws IOException {
                        gen.writeStartObject();
                        gen.writeStringField("value", String.valueOf(e.getValue()));
                        //gen.writeStringField("label", anEnum.getLabel());
                        gen.writeStringField("label", I18nUtil.ofMessage(e.getLabel()));
                        gen.writeEndObject();
                    }
                })
                //日期范围
                .serializerByType(DateRange.class, new JsonSerializer<DateRange>() {
                    @Override
                    public void serialize(DateRange value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                        gen.writeStartArray();
                        gen.writeString(value.getBegin().format(DE_FORMATTER));
                        gen.writeString(value.getEnd().format(DE_FORMATTER));
                        gen.writeEndArray();
                    }
                })
                .deserializerByType(DateRange.class, new JsonDeserializer<DateRange>() {
                    @Override
                    public DateRange deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
                        List<?> range = p.readValueAs(List.class);
                        if (Objects.nonNull(range) && range.size() == 2) {
                            return new DateRange(LocalDate.parse(String.valueOf(range.get(0)), DT_FORMATTER)
                                    , LocalDate.parse(String.valueOf(range.get(1)), DT_FORMATTER));
                        } else {
                            return null;
                        }
                    }
                })
                .serializerByType(DateTimeRange.class, new JsonSerializer<DateTimeRange>() {
                    @Override
                    public void serialize(DateTimeRange value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                        gen.writeStartArray();
                        gen.writeString(value.getBegin().format(DT_FORMATTER));
                        gen.writeString(value.getEnd().format(DT_FORMATTER));
                        gen.writeEndArray();
                    }
                })
                .deserializerByType(DateTimeRange.class, new JsonDeserializer<DateTimeRange>() {
                    @Override
                    public DateTimeRange deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
                        List<?> range = p.readValueAs(List.class);
                        if (Objects.nonNull(range) && range.size() == 2) {
                            return new DateTimeRange(LocalDateTime.parse(String.valueOf(range.get(0)), DT_FORMATTER)
                                    , LocalDateTime.parse(String.valueOf(range.get(1)), DT_FORMATTER));
                        } else {
                            return null;
                        }
                    }
                })
                .timeZone(TimeZone.getTimeZone("GMT+08:00"))
                //.timeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                .dateFormat(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"))
                //针对于Date类型，文本格式化
                .simpleDateFormat("yyyy-MM-dd HH:mm:ss")
                //所有为 null 的属性不序列化
                .serializationInclusion(JsonInclude.Include.NON_NULL)
                // 对象的属性值为""，序列化时不进行显示
                .serializationInclusion(JsonInclude.Include.NON_EMPTY);
        //DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES相当于配置，JSON串含有未知字段时，反序列化依旧可以成功
        //.failOnUnknownProperties(false);
        //序列化时的命名策略——驼峰命名法 (将userId变为 user_id)
        //.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
    }


    //    /**
    //     * 方法二
    //     * @param builder
    //     * @return
    //     */
    //    @Bean
    //    @Primary
    //    @ConditionalOnMissingBean(ObjectMapper.class)
    //    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    //        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
    //        SimpleModule simpleModule = new SimpleModule();
    //        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
    //        objectMapper.registerModule(simpleModule);
    //        return objectMapper;
    //    }

    public Jackson2ObjectMapperBuilder jacksonObjectMapper() {
        Jackson2ObjectMapperBuilder mapperBuilder = new Jackson2ObjectMapperBuilder();
        //针对于JDK新时间类。序列化时带有T的问题，自定义格式化字符串
        JavaTimeModule timeModule = new JavaTimeModule();
        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DT_FORMATTER));
        timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DE_FORMATTER));
        mapperBuilder.modules(timeModule);

        // jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        //默认关闭，将char[]数组序列化为String类型。若开启后序列化为JSON数组。
        mapperBuilder.featuresToEnable(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS);

        //默认开启，若map的value为null，则不对map条目进行序列化。(已废弃)。
        // 推荐使用：jacksonObjectMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL);
        //过时mapperBuilder.featuresToDisable(SerializationFeature.WRITE_NULL_MAP_VALUES);

        //默认开启，将Date类型序列化为数字时间戳(毫秒表示)。关闭后，序列化为文本表现形式(2019-10-23T01:58:58.308+0000)
        //若设置时间格式化。那么均输出格式化的时间类型。
        mapperBuilder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        //默认关闭，在类上使用@JsonRootName(value="rootNode")注解时是否可以包裹Root元素。
        // (https://blog.csdn.net/blueheart20/article/details/52212221)
        //  jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRAP_ROOT_VALUE);
        //默认开启：如果一个类没有public的方法或属性时，会导致序列化失败。关闭后，会得到一个空JSON串。
        mapperBuilder.featuresToDisable(SerializationFeature.FAIL_ON_EMPTY_BEANS);

        //默认关闭，即以文本(ISO-8601)作为Key，开启后，以时间戳作为Key
        mapperBuilder.featuresToEnable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);

        //默认禁用，禁用情况下，需考虑WRITE_ENUMS_USING_TO_STRING配置。启用后，ENUM序列化为数字
        mapperBuilder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_INDEX);

        //仅当WRITE_ENUMS_USING_INDEX为禁用时(默认禁用)，该配置生效
        //默认关闭，枚举类型序列化方式，默认情况下使用Enum.name()。开启后，使用Enum.toString()。注：需重写Enum的toString方法;
        mapperBuilder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);

        //默认开启，空Collection集合类型输出空JSON串。关闭后取消显示。(已过时)
        // 推荐使用serializationInclusion(JsonInclude.Include.NON_EMPTY);
        //过时 mapperBuilder.featuresToEnable(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);

        //默认关闭，当集合Collection或数组一个元素时返回："list":["a"]。开启后，"list":"a"
        //需要注意，和DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY 配套使用，要么都开启，要么都关闭。
        // jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);

        //默认关闭。打开后BigDecimal序列化为文本。(已弃用)，推荐使用JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN配置
        // jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
        //默认关闭，即使用BigDecimal.toString()序列化。开启后，使用BigDecimal.toPlainString序列化，不输出科学计数法的值。
        mapperBuilder.featuresToEnable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);

        /*
         * JsonGenerator.Feature的相关参数（JSON生成器）
         */

        //默认关闭，即序列化Number类型及子类为{"amount1":1.1}。开启后，序列化为String类型，即{"amount1":"1.1"}
        //过时 mapperBuilder.featuresToEnable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);

        /*
           反序列化
         */
        //默认关闭，当JSON字段为""(EMPTY_STRING)时，解析为普通的POJO对象抛出异常。开启后，该POJO的属性值为null。
        mapperBuilder.featuresToEnable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
        //默认关闭
        //jacksonObjectMapperBuilder.featuresToEnable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
        //默认关闭，若POJO中不含有JSON中的属性，则抛出异常。开启后，不解析该字段，而不会抛出异常。
        mapperBuilder.featuresToEnable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        return mapperBuilder;
    }
}
